package com.example.anclient;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Locale;
import java.util.Objects;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ANApplicationNetwork {
    private final Object connectionLock = new Object();
    private volatile boolean isConnectedSuccess = false;
    private SelectionKey  sendKey;
    private SocketChannel socketChannel;
    private final String address;
    private final MainActivity mainActivity;
    private final ExecutorService executor = Executors.newFixedThreadPool(2);
    private final Queue<ByteBuffer> writeQueue = new ConcurrentLinkedQueue<>();

    public ANApplicationNetwork(MainActivity mainActivity, String address) {
        this.mainActivity = mainActivity;
        this.address = address;
    }

    private void handleMessages(String msgs) {
        String[] tokens = msgs.split(",");

        for (String token : tokens) {
            String[] kv = token.split("=");
            if (kv.length != 2) {
                continue;
            }
            switch (kv[0]) {
                case "progress":
                    try {
                        float percent = Float.parseFloat(kv[1]);
                        mainActivity.setChargeProgress(percent);
                    } catch (NumberFormatException ignored) {
                    }
                    break;
                case "power":
                    try {
                        float percent = Float.parseFloat(kv[1]);
                        mainActivity.setChargePower(percent);
                    } catch (NumberFormatException ignored) {
                    }
                    break;
                case "voltage":
                    try {
                        float percent = Float.parseFloat(kv[1]);
                        mainActivity.setChargeVoltage(percent);
                    } catch (NumberFormatException ignored) {
                    }
                    break;
                case "temp":
                    try {
                        float percent = Float.parseFloat(kv[1]);
                        mainActivity.setTemperature(percent);
                    } catch (NumberFormatException ignored) {
                    }
                    break;
                case "balance":
                    try {
                        float percent = Float.parseFloat(kv[1]);
                        mainActivity.setChargeBalance(percent);
                    } catch (NumberFormatException ignored) {
                    }
                    break;
            }
        }

    }

    private void sendMessageToServer(String message) {
        executor.submit(() -> {
            synchronized (connectionLock) {
                while (!isConnectedSuccess) {
                    try {
                        connectionLock.wait();
                    } catch (InterruptedException e) {
                        Thread.currentThread().interrupt();
                        return;
                    }
                }
            }

            ByteBuffer buffer = ByteBuffer.wrap(message.getBytes());
            synchronized (this) {
                writeQueue.add(buffer);
                if (sendKey != null && sendKey.isValid()) {
                    sendKey.interestOps(sendKey.interestOps() | SelectionKey.OP_WRITE);
                    sendKey.selector().wakeup();
                }
            }
        });
    }

    public void sendControlMessage(String controls) {
        String message = "zncdz,devType=TERM,msgType=PUB,devID=redmi," + controls;
        sendMessageToServer(message);
    }

    public void registerToServer() {
        sendMessageToServer("zncdz,devType=TERM,msgType=REG,devID=redmi,cmd1");
    }

    public void subscribeDevice(String devID) {
        sendMessageToServer("zncdz,devType=TERM,msgType=SUB,devID=redmi,tgtID=" + devID);
    }

    public void startListening() {
        executor.submit(() -> {
            Selector selector;
            try {
                socketChannel = SocketChannel.open();
                socketChannel.configureBlocking(false);
                socketChannel.connect(new InetSocketAddress(address, 2933));
                selector = Selector.open();
                synchronized (this) {
                    sendKey = socketChannel.register(selector, SelectionKey.OP_CONNECT);
                }
                while (!Thread.interrupted()) {
                    selector.select();
                    Iterator<SelectionKey> keys = selector.selectedKeys().iterator();
                    while (keys.hasNext()) {
                        SelectionKey key = keys.next();
                        keys.remove();
                        if (key.isReadable()) {
                            handleRead(key);
                        } else if (key.isWritable()) {
                            handleWrite(key);
                        } else if (key.isConnectable()) {
                            handleConnect(key);
                        }
                    }
                }
            } catch (IOException e) {
                mainActivity.setErrorMessageTitle("SocketChannel 创建失败！");
            }
        });
    }

    private void handleConnect(SelectionKey key) throws IOException {
        SocketChannel client = (SocketChannel) key.channel();
        if (client.finishConnect()) {
            key.interestOps(SelectionKey.OP_READ);
            synchronized (connectionLock) {
                isConnectedSuccess = true;
                connectionLock.notifyAll();
            }
        } else {
            key.cancel();
            client.close();
        }
    }

    private void handleRead(SelectionKey key) throws IOException {
        SocketChannel client = (SocketChannel) key.channel();
        ByteBuffer buffer = ByteBuffer.allocate(1024);
        int bytesRead = client.read(buffer);
        if (bytesRead == -1) {
            client.close();
            key.cancel();
            return;
        }
        buffer.flip();
        byte[] data = new byte[buffer.remaining()];
        buffer.get(data);
        handleMessages(new String(data));
    }

    private void handleWrite(SelectionKey key) throws IOException {
        SocketChannel client = (SocketChannel) key.channel();
        ByteBuffer buffer;
        synchronized (this) {
            buffer = writeQueue.peek();
        }
        if (buffer == null) {
            key.interestOps(SelectionKey.OP_READ);
            return;
        }
        client.write(buffer);
        if (!buffer.hasRemaining()) {
            synchronized (this) {
                writeQueue.poll();
                if (writeQueue.isEmpty()) {
                    key.interestOps(SelectionKey.OP_READ);
                }
            }
        }
    }
}
